package com.gitee.wsl.ext.list

import arrow.core.Option

inline fun <T, V> List<T>.firstMapOrNull(predicate: (T) -> V?): V? {
    for (element in this) {
        @Suppress("UnnecessaryVariable")  // wants to inline, but it's unreadable that way
        val found = predicate(element) ?: continue
        return found
    }
    return null
}

/**
 * The same as a regular [mapNotNull], but [transform] block contains the previous value of the list.
 */
fun <T, R> List<T>.mapNotNull(transform: (prev: R?, value: T) -> R?): List<R> {
    val dst = ArrayList<R>()
    forEach { value -> transform(dst.lastOrNull(), value)?.let { dst.add(it) } }
    return dst
}


fun <T: Any> List<T>.mapOrSame(f: (T) -> T): List<T> {
    var res: MutableList<T>? = null

    for (i in this.indices) {
        val v = this[i]
        val v2 = f(v)
        if (res == null && v2 !== v) {
            res = ArrayList(this.size)
            for (j in 0 until i) {
                res.add(this[j])
            }
        }
        res?.add(v2)
    }

    return res ?: this
}

fun <T: Any> List<T>.mapIndexedOrSame(f: (Int, T) -> T): List<T> {
    var res: MutableList<T>? = null

    for (i in this.indices) {
        val v = this[i]
        val v2 = f(i, v)
        if (res == null && v2 !== v) {
            res = ArrayList<T>(this.size)
            for (j in 0 until i) {
                res.add(this[j])
            }
        }
        res?.add(v2)
    }

    return res ?: this
}

/**
 * Returns a list containing only the non-None results of applying the given transform function
 * to each element in the original collection.
 */
inline fun <A, B> List<A>.mapNotNone(f: (A) -> Option<B>): List<B> =
    this.flatMap { f(it).fold(::emptyList, ::listOf) }